﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;

namespace SPOffline.Web
{
    /// <summary>
    /// A custom admin page for setting a web application online / offline.
    /// </summary>
    public class WebApplicationOffline : Page
    {
        #region Controls

        protected WebApplicationSelector webAppSelector;
        protected InputFormRadioButton onlineRadio;
        protected InputFormRadioButton offlineRadio;
        protected InputFormTextBox offlineMessage;
        protected InputFormRequiredFieldValidator offlineMessageReq;
        protected RadioButton timeNowRadio;
        protected RadioButton timeSpecifiedRadio;
        protected DateTimeControl scheduleTime;
        protected Button submitButton;
        protected Label errorMessage;
        protected Label webAppJobStatus;

        #endregion Controls

        #region Event Handlers

        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.Load"/> event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.EventArgs"/> object that contains the event data.</param>
        protected override void OnLoad(EventArgs e)
        {
            try
            {
                SPContext.Current.Web.AllowUnsafeUpdates = true;

                if (!IsPostBack)
                {
                    // configure schedule
                    SetDefaultSchedule();

                    // load from persisted settings on first request
                    LoadControlStateFromSettings();

                    // display job (if any)
                    DisplayScheduledTimerJobs();

                    // set control state
                    ConfigureControls();
                }

                // attach event handlers
                submitButton.Click += submitButton_Click;
            }
            catch(Exception ex)
            {
                HandleException(ex);
            }
        }

        /// <summary>
        /// Handles the ContextChange event of the webAppSelector control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void webAppSelector_ContextChange(object sender, EventArgs e)
        {
            try
            {
                // load persisted settings
                LoadControlStateFromSettings();

                // display jobs
                DisplayScheduledTimerJobs();

                // configure controls
                ConfigureControls();

            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        /// <summary>
        /// Handles the Click event of the submitButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void submitButton_Click(object sender, EventArgs e)
        {
            try
            {

                // update the persisted settings
                SaveSettingsFromControlState();

                // schedule job
                ScheduleTimerJob();

                // redirect to Applications page
                SPUtility.Redirect("/_admin/applications.aspx", SPRedirectFlags.UseSource, Context);
            }
            catch(Exception ex)
            {
                HandleException(ex);
            }
        }

        #endregion Event Handlers

        #region Helper Methods

        /// <summary>
        /// Handles the exception.
        /// </summary>
        /// <param name="exception">The exception.</param>
        private void HandleException(Exception exception)
        {
            //TODO: log erroor
            errorMessage.Text = exception.Message;
#if DEBUG
            errorMessage.Text += exception.StackTrace;
#endif

        }

        /// <summary>
        /// Sets the state of the controls.
        /// </summary>
        protected void ConfigureControls()
        {
            submitButton.Enabled = (webAppSelector.CurrentItem != null);
        }

        /// <summary>
        /// Loads the control state from settings.
        /// </summary>
        private void LoadControlStateFromSettings()
        {
            var webApplication = webAppSelector.CurrentItem;
            if (webApplication == null)
            {
                offlineRadio.Checked = false;
                onlineRadio.Checked = true;
                offlineMessage.Text = string.Empty;
            }
            else
            {
                var settings = SPOfflineSettings.GetSettings(webApplication);
                onlineRadio.Checked = settings.IsOnline;
                offlineRadio.Checked = !onlineRadio.Checked;
                offlineMessage.Text = settings.OfflineMessage;
            }
        }

        /// <summary>
        /// Saves the state of the settings from control.
        /// </summary>
        private void SaveSettingsFromControlState()
        {
            var webApplication = webAppSelector.CurrentItem;
            if (webApplication == null)
            {
                throw new InvalidOperationException("Web application is null.");
            }

            var settings = SPOfflineSettings.GetSettings(webApplication);
            settings.IsOnline = onlineRadio.Checked;
            settings.OfflineMessage = offlineMessage.Text;
            settings.Update();
        }

        /// <summary>
        /// Displays information about the scheduled job for the current web application.
        /// </summary>
        private void DisplayScheduledTimerJobs()
        {
            var webApplication = webAppSelector.CurrentItem;
            if (webApplication != null)
            {
                // get settings
                var settings = SPOfflineSettings.GetSettings(webApplication);

                // get jobs scheduled for this app
                var jobs = GetScheduledTimerJobs(webApplication);

                var jobStatus = new StringBuilder();
                if (jobs != null && jobs.Count() > 0)
                {
                    foreach (var job in jobs)
                    {
                        jobStatus.AppendFormat("A timer job to set this web application {0} is scheduled for {1}.<br/>",
                                               settings.IsOnline ? "online" : "offline",
                                               job.Schedule.NextOccurrence(DateTime.Now));
                    }
                    jobStatus.AppendFormat(
                        "If you continue, the timer job{0} will be deleted and a new one will be created.",
                        jobs.Count() > 1 ? "s" : "");

                    webAppJobStatus.Text = jobStatus.ToString();
                }
            }
        }

        /// <summary>
        /// Gets the scheduled timer jobs.
        /// </summary>
        /// <returns>The list of timer jobs scheduled for the specified application.</returns>
        private static IEnumerable<SPJobDefinition> GetScheduledTimerJobs(SPWebApplication webApplication)
        {
            if (webApplication != null)
            {
                return (from job in webApplication.JobDefinitions where job.Name == Constants.JobName select job);
            }
            return null;
        }

        /// <summary>
        /// Deletes the scheduled timer jobs.
        /// </summary>
        /// <param name="webApplication">The web application.</param>
        private static void DeleteScheduledTimerJobs(SPWebApplication webApplication)
        {
            var jobs = GetScheduledTimerJobs(webApplication);
            if (jobs != null)
            {
                jobs.ToList().ForEach(job => job.Delete());
            }
        }

        /// <summary>
        /// Schedules the timer job.
        /// </summary>
        private void ScheduleTimerJob()
        {
            var webApplication = webAppSelector.CurrentItem;
            if (webApplication == null)
            {
                throw new InvalidOperationException("Web application is null.");
            }

            // get scheduled time
            var schedTime = DateTime.Now;
            if (timeSpecifiedRadio.Checked)
            {
                schedTime = scheduleTime.SelectedDate;
                if (DateTime.Compare(schedTime, DateTime.Now) <= 0)
                {
                    throw new ArgumentException("The scheduled time cannot be in the past.");
                }
            }

            // if the job is scheduled already, delete it
            DeleteScheduledTimerJobs(webApplication);

            // schedule the job
            var schedule = new SPOneTimeSchedule { Time = schedTime };
            
            var newJob = new SPOfflineJob(webApplication) { Schedule = schedule};
            newJob.Update(true);
            
        }

        /// <summary>
        /// Sets the default schedule.
        /// </summary>
        protected void SetDefaultSchedule()
        {
            scheduleTime.MaxDate = DateTime.Now.AddMonths(1);
            scheduleTime.MinDate = DateTime.Now;
            scheduleTime.SelectedDate = DateTime.Now;
        }
        
        #endregion Helper Methods

    }

}